package com.fanxuankai.boot.apollo.openapi.component;

import com.ctrip.framework.apollo.openapi.client.ApolloOpenApiClient;
import com.ctrip.framework.apollo.openapi.client.exception.ApolloOpenApiException;
import com.ctrip.framework.apollo.openapi.dto.OpenItemDTO;
import com.ctrip.framework.apollo.openapi.dto.OpenNamespaceDTO;
import com.fanxuankai.boot.apollo.openapi.autoconfigure.ApolloConfigSyncProperties;
import com.fanxuankai.boot.apollo.openapi.autoconfigure.ApolloOpenApiProperties;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author fanxuankai
 */
public class ApolloConfigSynchronizer implements ApplicationRunner {
    private final ApolloOpenApiClient client;
    private final ApolloOpenApiProperties properties;
    private final ApolloConfigSyncProperties syncProperties;
    private static final Logger LOGGER = LoggerFactory.getLogger(ApolloConfigSynchronizer.class);

    public ApolloConfigSynchronizer(ApolloOpenApiClient client, ApolloOpenApiProperties apolloOpenApiProperties,
                                    ApolloConfigSyncProperties apolloOpenApiConfigSyncProperties) {
        this.client = client;
        this.properties = apolloOpenApiProperties;
        this.syncProperties = apolloOpenApiConfigSyncProperties;
    }

    @Override
    public void run(ApplicationArguments args) {
        Consumer<OpenItemDTO> deleteItemConsumer = item -> {
            try {
                client.removeItem(properties.getAppId(), properties.getEnv(),
                        properties.getClusterName(), properties.getNamespace(), item.getKey(),
                        syncProperties.getLastModifiedBy());
                LOGGER.info("Item removed successfully, key: {}", item.getKey());
            } catch (Throwable t) {
                if (t.getCause() instanceof ApolloOpenApiException) {
                    ApolloOpenApiException apolloOpenApiException = (ApolloOpenApiException) t.getCause();
                    // 重复删除配置会有 404 异常
                    if (apolloOpenApiException.getStatus() == HttpStatus.SC_NOT_FOUND) {
                        return;
                    }
                }
                LOGGER.error("Item removed failed, key: " + item.getKey(), t);
            }
        };
        OpenNamespaceDTO existsNamespace = client.getNamespace(properties.getAppId(), properties.getEnv(),
                properties.getClusterName(), properties.getNamespace());
        // 清除空白行
        existsNamespace.getItems().removeIf(o -> StringUtils.isEmpty(o.getKey()));
        if (CollectionUtils.isEmpty(syncProperties.getItems())) {
            // 清空配置
            existsNamespace.getItems().forEach(deleteItemConsumer);
            return;
        }
        Map<String, OpenItemDTO> existsItems = existsNamespace.getItems().stream()
                .collect(Collectors.toMap(OpenItemDTO::getKey, Function.identity()));
        Set<String> itemKeys = syncProperties.getItems().keySet();
        existsNamespace.getItems()
                .stream()
                .filter(item -> !itemKeys.contains(item.getKey()))
                .forEach(deleteItemConsumer);
        Date now = new Date();
        for (Map.Entry<String, List<String>> entry : syncProperties.getItems().entrySet()) {
            String key = entry.getKey();
            List<String> valueAndComment = entry.getValue();
            String value = valueAndComment.get(0);
            String comment = null;
            if (valueAndComment.size() > 1) {
                comment = valueAndComment.get(1);
            }
            OpenItemDTO existsItem = existsItems.get(entry.getKey());
            if (existsItem != null) {
                // comment 存在空字符的情况
                if (StringUtils.isEmpty(existsItem.getComment())) {
                    existsItem.setComment(null);
                }
                if (Objects.equals(existsItem.getValue(), value)
                        && Objects.equals(existsItem.getComment(), comment)) {
                    // 忽略未发生变化的配置
                    LOGGER.info("Ignore unchanged item, key: {}, value: {}, comment: {}", key, value, comment);
                    continue;
                }
            }
            OpenItemDTO dto = new OpenItemDTO();
            dto.setDataChangeCreatedBy(syncProperties.getCreatedBy());
            dto.setDataChangeCreatedTime(now);
            dto.setDataChangeLastModifiedBy(syncProperties.getLastModifiedBy());
            dto.setDataChangeLastModifiedTime(now);
            dto.setKey(key);
            dto.setValue(value);
            dto.setComment(comment);
            // 新建或修改配置
            client.createOrUpdateItem(properties.getAppId(), properties.getEnv(),
                    properties.getClusterName(), properties.getNamespace(), dto);
            LOGGER.info("Item created or modified successfully: {}", dto);
        }
    }
}
